一、前言

二、大纲

  • sleep()
  • yield()
  • join()
  • 线程优先级
  • 高效结束线程

三、详述

3.1、线程睡眠——sleep()

  • 注意 :

    • ①、sleep()静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。
    • ②、Java线程调度是Java多线程的核心, 但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。因为

      sleep() -> 阻塞状态 -> 就绪状态 -> 运行状态。
      而就绪状态进入到运行状态,是由系统控制的
      
      

3.2、线程礼让——yield()

  • 性质:

    • 一个静态的方法

      • yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,所以有可能刚进入就绪状态,又被调度到运行状态
      • 让出cpu资源给其他的线程。
  • sleep()的区别

    • ①、sleep()方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。
      yield()方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
    • ③、sleep()声明抛出了InterruptedException,所以调用sleep()方法的时候要捕获该异常,或者显示声明抛出该异常。
      yield()方法则没有声明抛出任务异常。
    • ③、sleep()方法比yield()方法有更好的可移植性,通常要依靠yield()方法来控制并发线程的执行。

3.3、线程合并 (插队)——join()

  • 性质:线程1使用join()后,线程2必须等待线程1执行完毕才能执行!,常常在一个线程的内部的run()方法,进行另一个线程的join()(插队)
  • 重载版本

    • void join(); 当前线程等该加入该线程后面,等待该线程终止。
    • void join(long millis)

      • 当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度。
    • void join(long millis,int nanos)

      • 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度

3.4、线程优先级 ——Priority

  • 性质:

    • 每个线程执行时都有一个优先级的属性
    • 优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。
    • 线程休眠类似,线程的优先级仍然无法保障线程的执行次序
    • 每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级
  • 使用:Thread类提供了

    • setPriority(int newPriority)设置1个指定线程的优先级
    • getPriority()返回1个指定线程的优先级
    • 范围是1 ~ 10之间
    • 推荐使用Thread类的静态成员变量进行设置,这样才能保证程序最好的可移植性。

           MAX_PRIORITY  = 10
           MIN_PRIORITY  = 1 
           NORM_PRIORITY = 5
           
      

3.5、守护线程

  • 性质:

    • 它为用户线程提供后台支持任务的服务。
    • 它在生命中没有为服务用户线程而发挥作用。
    • 它的生命取决于用户线程。它是一个低优先级的线程。
    • 用户线程都执行完,JVM会终止守护程序线程
  • 应用实例(守护线程通常用于执行一些后台作业)

    • JVM的垃圾回收、内存管理等线程都是守护线程
    • 数据库连接池,连接池本身也包含着很多后台(守护)线程,监控连接个数、超时时间、状态等等。
    • 应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。
    • 如果将这个播放背景音乐的线程设定为守护线程,那么在用户请求退出的时候,不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了
  • 使用方法

     `public final void setDaemon(boolean on);`
     
      Thread t = new Thread( () -> {
          public void run() { //do something; }
      });
      t.setDaemon(true);    //该方法必须在 start() 启动线程前调用。
      t.start();
    • 可能的异常

      IllegalThreadStateException - 如果该线程处于活动状态。     
      SecurityException - 如果当前线程无法修改该线程。
      
      

3.6、如何高效地结束一个线程!

  • 一般情况下,线程不执行完任务不会退出,但是在有些场景下,我们需要手动控制线程中断结束任务。那什么时候需要关闭一个线程?下面简单的举例情况: 比如我们会启动多个线程做同一件事。

    • 抢12306的火车票,我们可能开启多个线程从多个渠道买火车票,只要有一个渠道买到了,我们会通知取消其他渠道。
    • 这个时候需要关闭其他线程很多线程的运行模式是死循环,比如在生产者/消费者模式中,消费者主体就是一个死循环,它不停的从队列中接受任务,执行任务,在停止程序时,我们需要一种”优雅”的方法以关闭该线程在一些场景中
    • 从第三方服务器查询一个结果,我们希望在限定的时间内得到结果,如果得不到,我们会希望取消该任务。
    • 总之,很多情况下我们都有关闭一个线程的需求。
  • TipThread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!
  • 常用方法

    • 1、正常执行完run()方法,然后结束掉

      
             class MyThread extends Thread { 
                 int i=0; 
                 @Override
                 public void run() { 
                    while (true) 
                    { 
                       if (i == 10) 
                          break; 
                       i++; 
                       System.out.println(i); 
                    } 
                 } 
            } 
    • 2、控制循环条件和判断条件的标识符来结束掉线程

             class MyThread extends Thread { 
                 int i = 0; 
                 boolean next = true; 
                 @Override
                 public void run() { 
                    while (next) 
                    { 
                       if(i == 10) 
                           next = false; 
                       i++; 
                       System.out.println(i); 
                    } 
                 } 
             }
    • 3、使用interrupt()这个巧妙的方式结束掉这个线程

      • (1)2种方法的标识符来结束一个线程,是一个不错的方法,但如果,该线程是处于sleep()、wait()、join()的状态的时候,while循环就不会执行,那么我们的标识符就无用武之地了,
      • (2) 我们看看sleep()、wait()、join()方法的声明:

        public final void wait() throws InterruptedException 
        public static native void sleep(long millis) throws InterruptedException 
        public final void join() throws InterruptedException
      • 三者有一个共同点,都抛出了一个InterruptedException的异常。
      • (3) 在什么时候会产生这样一个异常呢?

        • 当一个线程处于sleep()、wait()、join()这三种状态之一的时候,如果此时它的中断状态为true,JVM会先将该线程的中断标识位清除(改为false),然后抛出一个InterruptedException的异常。
      • (4) 例子

         public class Test {
             public static void main(String[] args) throws InterruptedException    
             {
                 MyThread thread=new MyThread();
                 thread.start();
             }
         }
         class MyThread extends Thread {
             int i = 1;
             @Override
             public void run()
             {
                 while (true)
                 {
                     System.out.println(i);
                     System.out.println(this.isInterrupted());
                     try{
                         System.out.println("我马上去sleep了");
                         Thread.sleep(2000);
                         this.interrupt(); //interrupt()方法将中断状态设置为true
                     }
                     catch (InterruptedException e){
                         System.out.println("异常捕获了" + this.isInterrupted());
                         return;
                     }
                     i++;
                 }
             }
         }结果:
         1. 1  
         2. false  
         3. 我马上去sleep了  
         4. 2  
         5. true  
         6. 我马上去sleep了  
         7. 异常捕获了false 
          (1)首先执行第一次while循环,在第一次循环中,睡眠2秒,然后将中断状态设置为true。
          (2)当进入到第二次循环的时候,中断状态就是第一次设置的true,当它再次进入sleep的时候,
             马上就抛出了InterruptedException异常
          (3)然后被我们捕获了。
          (4)然后中断状态又被重新自动设置为false了(从最后一条输出可以看出来)
        
      • (5) 中断说明

        • interrupt()方法只是改变中断状态,不会中断一个正在运行的线程。需要用户自己去监视线程的状态为并做处理
        • 线程中断后会抛出interruptedException的方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。

wdt0x01
29 声望2 粉丝

引用和评论

0 条评论